package com.googlecode.gaal.vis;

import java.io.IOException;
import java.util.List;
import java.util.Map;

import com.googlecode.gaal.data.api.IntSequence;
import com.googlecode.gaal.data.api.SymbolTable;
import com.googlecode.gaal.suffix.api.EmbeddedSuffixTree;
import com.googlecode.gaal.suffix.api.EnhancedSuffixArray;
import com.googlecode.gaal.suffix.api.IntervalTree;
import com.googlecode.gaal.suffix.api.IntervalTree.Interval;
import com.googlecode.gaal.suffix.api.LinearizedSuffixTree;
import com.googlecode.gaal.suffix.api.SuffixArray;
import com.googlecode.gaal.vis.api.Drawing;
import com.googlecode.gaal.vis.impl.TikzConstants;
import com.googlecode.gaal.vis.impl.TikzIntervalDrawing;
import com.googlecode.gaal.vis.impl.TikzReducedIntervalDrawing;
import com.googlecode.gaal.vis.impl.TikzVectorDrawing;

public class LaTeXVisualizer {

    private static String[] buildBwtTable(int[] suffixTable, IntSequence sequence, SymbolTable<String> symbolTable) {
        String[] bwtTable = new String[suffixTable.length];
        for (int i = 0; i < suffixTable.length; i++) {
            int prev = suffixTable[i] - 1;
            if (prev == -1)
                prev = suffixTable[suffixTable.length - 1];
            bwtTable[i] = symbolTable.toToken(sequence.get(prev));
        }
        return bwtTable;
    }

    public static <S> void visualizeSequence(Appendable buffer, IntSequence sequence, SymbolTable<S> symbolTable)
            throws IOException {

        Drawing drawing = new TikzReducedIntervalDrawing(buffer, null, null, 0.65, 0.65, 26, 0);
        TableVisualizer<S> tableVisualizer = new TableVisualizer<S>(sequence, symbolTable);
        tableVisualizer.visualizeSequence(drawing);
        drawing.flush();
    }

    public static <S> void visualizeSubsequence(Appendable buffer, IntSequence sequence, SymbolTable<S> symbolTable,
            int... indices) throws IOException {

        Drawing drawing = new TikzReducedIntervalDrawing(buffer, null, null, 0.65, 0.65, 26, 0);
        TableVisualizer<S> tableVisualizer = new TableVisualizer<S>(sequence, symbolTable);
        tableVisualizer.visualizeSubsequence(drawing, indices);
        drawing.flush();
    }

    public static <S> void visualizeComplexSubsequence(Appendable buffer, IntSequence sequence,
            SymbolTable<S> symbolTable, int... indices) throws IOException {

        Drawing drawing = new TikzReducedIntervalDrawing(buffer, null, null, 0.65, 0.65, 26, 0);
        TableVisualizer<S> tableVisualizer = new TableVisualizer<S>(sequence, symbolTable);
        tableVisualizer.visualizeComplexSubsequence(drawing, indices);
        drawing.flush();
    }

    public static <S> void visualizeSuffixes(Appendable buffer, String caption, String label, IntSequence sequence,
            SymbolTable<S> symbolTable) throws IOException {

        Drawing drawing = new TikzReducedIntervalDrawing(buffer, caption, label, 0.65, 0.65, 26, 0);
        TableVisualizer<S> tableVisualizer = new TableVisualizer<S>(sequence, symbolTable);
        tableVisualizer.visualizeSuffixes(drawing);
        drawing.flush();
    }

    public static <S> void visualizeSuffixTable(Appendable buffer, String caption, String label, SuffixArray sa,
            IntSequence sequence, SymbolTable<S> symbolTable) throws IOException {

        Drawing drawing = new TikzReducedIntervalDrawing(buffer, caption, label, 0.65, 0.65, 26, 0);
        TableVisualizer<S> tableVisualizer = new TableVisualizer<S>(sequence, symbolTable);
        tableVisualizer.visualizeSuffixTable(drawing, sa);
        drawing.flush();
    }

    public static <S> void visualizeEmbeddedSuffixes(Appendable buffer, String caption, String label, SuffixArray sa,
            Interval interval, int windowSize, SymbolTable<S> symbolTable) throws IOException {

        Drawing drawing = new TikzReducedIntervalDrawing(buffer, caption, label, "ht!", 0.65, 0.65, 26, 0);
        TableVisualizer<S> tableVisualizer = new TableVisualizer<S>(sa.getSequence(), symbolTable);
        tableVisualizer.visualizeEmbeddedSuffixes(drawing, sa, interval, windowSize);
        drawing.flush();
    }

    public static <S> void visualizeWindow(Appendable buffer, String caption, String label, SuffixArray sa,
            Interval interval, int windowSize, SymbolTable<S> symbolTable) throws IOException {

        Drawing drawing = new TikzReducedIntervalDrawing(buffer, caption, label, "ht!", 0.65, 0.65, 26, 0);
        TableVisualizer<S> tableVisualizer = new TableVisualizer<S>(sa.getSequence(), symbolTable);
        tableVisualizer.visualizeWindow(drawing, sa, interval, windowSize);
        drawing.flush();
    }

    public static <S> void visualizeEmbeddedSuffixesInWindow(Appendable buffer, String caption, String label,
            SuffixArray sa, Interval interval, int windowSize, SymbolTable<S> symbolTable) throws IOException {

        Drawing drawing = new TikzReducedIntervalDrawing(buffer, caption, label, "ht!", 0.65, 0.65, 26, 0);
        TableVisualizer<S> tableVisualizer = new TableVisualizer<S>(sa.getSequence(), symbolTable);
        tableVisualizer.visualizeEmbeddedSuffixesInWindow(drawing, sa, interval, windowSize);
        drawing.flush();
    }

    public static <S> void visualizeEmbeddedSuffixTable(Appendable buffer, String caption, String label,
            SuffixArray sa, SymbolTable<S> symbolTable) throws IOException {

        Drawing drawing = new TikzReducedIntervalDrawing(buffer, caption, label, "ht!", 0.65, 0.65, 26, 0);
        TableVisualizer<S> tableVisualizer = new TableVisualizer<S>(sa.getSequence(), symbolTable);
        tableVisualizer.visualizeEmbeddedSuffixTable(drawing, sa);
        drawing.flush();
    }

    public static <S> void visualizeLcpInSuffixTable(Appendable buffer, String caption, String label, SuffixArray sa,
            SymbolTable<S> symbolTable, boolean placeHere) throws IOException {

        Drawing drawing = null;
        if (placeHere)
            drawing = new TikzReducedIntervalDrawing(buffer, caption, label, "H", 0.65, 0.65, 26, 0);
        else
            drawing = new TikzReducedIntervalDrawing(buffer, caption, label, 0.65, 0.65, 26, 0);
        TableVisualizer<S> tableVisualizer = new TableVisualizer<S>(sa.getSequence(), symbolTable);
        tableVisualizer.visualizeLcpInSuffixTable(drawing, sa);
        drawing.flush();
    }

    public static void visualizeSuffixArray(Appendable buffer, String caption, String label, SuffixArray sa,
            SymbolTable<String> symbolTable) throws IOException {

        Drawing drawing = new TikzReducedIntervalDrawing(buffer, caption, label, 0.65, 0.65, 26, 0);
        TableVisualizer<String> tableVisualizer = new TableVisualizer<String>(sa.getSequence(), symbolTable);
        tableVisualizer.visualizeTable(drawing, sa.getSuffixTable(), "suftab", 6, TikzConstants.RED_CELL);
        tableVisualizer.visualizeTable(drawing, sa.getLcpTable(), "lcptab", 6, TikzConstants.GREEN_CELL);
        drawing.flush();
    }

    public static void visualizeInverseSuffixTable(Appendable buffer, String caption, String label, SuffixArray sa,
            SymbolTable<String> symbolTable) throws IOException {

        Drawing drawing = new TikzReducedIntervalDrawing(buffer, caption, label, "H", 0.65, 0.65, 26, 0);
        TableVisualizer<String> tableVisualizer = new TableVisualizer<String>(sa.getSequence(), symbolTable);
        tableVisualizer.visualizeIndexTable(drawing, sa.getSuffixTable().length, "index", 6,
                TikzConstants.LIGHT_BLUE_CELL);
        tableVisualizer.visualizeTable(drawing, sa.getSuffixTable(), "suftab", 6, TikzConstants.RED_CELL);
        tableVisualizer.visualizeTable(drawing, sa.getInverseSuffixTable(), "saftab$^{-1}$", 6,
                TikzConstants.GREEN_CELL);
        drawing.flush();
    }

    public static <S> void visualizeInterval(Appendable buffer, String caption, String label, SuffixArray sa,
            Interval interval, int length, int windowSize, SymbolTable<S> symbolTable) throws IOException {

        Drawing drawing = new TikzReducedIntervalDrawing(buffer, caption, label, 0.65, 0.65, 26, 0);
        TableVisualizer<S> tableVisualizer = new TableVisualizer<S>(sa.getSequence(), symbolTable);
        tableVisualizer.visualizeInterval(drawing, interval, length, windowSize);
        drawing.flush();
    }

    public static void visualizeEnhancedSuffixArray(Appendable buffer, String caption, String label,
            EnhancedSuffixArray esa, SymbolTable<String> symbolTable) throws IOException {

        Drawing drawing = new TikzReducedIntervalDrawing(buffer, caption, label, 0.65, 0.65, 26, 0);
        TableVisualizer<String> tableVisualizer = new TableVisualizer<String>(esa.getSequence(), symbolTable);
        tableVisualizer.visualizeTable(drawing, esa.getSuffixTable(), "suftab", 6, TikzConstants.RED_CELL);
        tableVisualizer.visualizeTable(drawing, esa.getLcpTable(), "lcptab", 6, TikzConstants.GREEN_CELL);
        tableVisualizer.visualizeTable(drawing, esa.getChildTable(), "cldtab", 6, TikzConstants.BLUE_CELL);
        drawing.flush();
    }

    public static void visualizeLinearizedSuffixTree(Appendable buffer, String caption, String label,
            LinearizedSuffixTree lst, SymbolTable<String> symbolTable) throws IOException {

        Drawing drawing = new TikzReducedIntervalDrawing(buffer, caption, label, 0.65, 0.65, 26, 0);
        TableVisualizer<String> tableVisualizer = new TableVisualizer<String>(lst.getSequence(), symbolTable);
        tableVisualizer.visualizeTable(drawing, lst.getSuffixTable(), "suftab", 6, TikzConstants.RED_CELL);
        tableVisualizer.visualizeTable(drawing, lst.getLcpTable(), "lcptab", 6, TikzConstants.GREEN_CELL);
        tableVisualizer.visualizeTable(drawing, lst.getChildTable(), "newcldtab", 6, TikzConstants.BLUE_CELL);
        drawing.flush();
    }

    public static void visualizeEmbeddedSuffixTree(Appendable buffer, String caption, String label,
            EmbeddedSuffixTree est, SymbolTable<String> symbolTable) throws IOException {

        Drawing drawing = new TikzReducedIntervalDrawing(buffer, caption, label, "H", 0.65, 0.65, 26, 0);
        TableVisualizer<String> tableVisualizer = new TableVisualizer<String>(est.getSequence(), symbolTable);
        tableVisualizer.visualizeTable(drawing, est.getSuffixTable(), "suftab", 6, TikzConstants.RED_CELL);
        tableVisualizer.visualizeTable(drawing, est.getLcpTable(), "lcptab", 6, TikzConstants.GREEN_CELL);
        tableVisualizer.visualizeTable(drawing, est.getChildTable(), "newcldtab", 6, TikzConstants.BLUE_CELL);
        drawing.flush();
    }

    public static <E extends Interval, T extends SuffixArray & IntervalTree<E>> void visualizeMaximalIntervals(
            Appendable buffer, String caption, String label, T tree, SymbolTable<String> symbolTable)
            throws IOException {

        Drawing drawing = new TikzReducedIntervalDrawing(buffer, caption, label, 0.65, 0.65, 26, 0);
        TreeVisualizer<String> treeVisualizer = new TreeVisualizer<String>(symbolTable, true);
        treeVisualizer.visualizeTree(drawing, tree);
        TableVisualizer<String> tableVisualizer = new TableVisualizer<String>(tree.getSequence(), symbolTable);
        tableVisualizer.visualizeTable(drawing, buildBwtTable(tree.getSuffixTable(), tree.getSequence(), symbolTable),
                "bwttab", 6, TikzConstants.ORANGE_CELL);
        tableVisualizer.visualizeSuffixIntervals(drawing, tree, treeVisualizer.getStyleMap());
        drawing.flush();
    }

    public static <S, E extends Interval> void visualizeSuffixTree(Appendable buffer, String caption, String label,
            IntervalTree<E> tree, SymbolTable<S> symbolTable) throws IOException {
        Drawing drawing = new TikzReducedIntervalDrawing(buffer, caption, label, 0.65, 0.65, 26, 2);
        TreeVisualizer<S> treeVisualizer = new TreeVisualizer<S>(symbolTable, false);
        treeVisualizer.visualizeTree(drawing, tree);
        drawing.flush();
    }

    public static <S, E extends Interval, T extends SuffixArray & IntervalTree<E>> void visualizeIntervalTree(
            Appendable buffer, String caption, String label, T tree, int[] childTable, SymbolTable<S> symbolTable)
            throws IOException {

        Drawing drawing = new TikzReducedIntervalDrawing(buffer, caption, label, 0.65, 0.65, 26, 0);
        TreeVisualizer<S> treeVisualizer = new TreeVisualizer<S>(symbolTable, true);
        treeVisualizer.visualizeTree(drawing, tree);
        TableVisualizer<S> tableVisualizer = new TableVisualizer<S>(tree.getSequence(), symbolTable);
        tableVisualizer.visualizeTable(drawing, tree.getSuffixTable(), "suftab", 6, TikzConstants.RED_CELL);
        tableVisualizer.visualizeTable(drawing, tree.getLcpTable(), "lcptab", 6, TikzConstants.GREEN_CELL);
        if (childTable != null) {
            tableVisualizer.visualizeTable(drawing, childTable, "cldtab", 6, TikzConstants.BLUE_CELL);
        }
        tableVisualizer.visualizeSuffixIntervals(drawing, tree, treeVisualizer.getStyleMap());
        drawing.flush();
    }

    public static <S> void visualizeChildTable(Appendable buffer, String caption, String label,
            LinearizedSuffixTree lst, SymbolTable<S> symbolTable) throws IOException {
        Drawing drawing;
        TreeVisualizer<S> treeVisualizer = new TreeVisualizer<S>(symbolTable, true);
        Map<Interval, Integer> terminals;
        TableVisualizer<S> tableVisualizer;
        int terminalsNum = -1;
        int depth = 0;
        int[] usedCells = new int[lst.getSequence().size()];
        while (terminalsNum != 0) {
            drawing = new TikzIntervalDrawing(buffer, String.format(caption, depth), String.format(label, depth), 0.65,
                    0.65, 26);
            terminals = treeVisualizer.visualizeTree(drawing, lst, depth);
            tableVisualizer = new TableVisualizer<S>(lst.getSequence(), symbolTable);
            tableVisualizer.visualizeChildTable(drawing, lst, terminals, usedCells, depth);
            drawing.flush();
            depth++;
            terminalsNum = terminals.size();
        }
    }

    public static <S> void visualizeChildTable(Appendable buffer, String caption, String label,
            EnhancedSuffixArray esa, SymbolTable<S> symbolTable) throws IOException {
        Drawing drawing;
        TreeVisualizer<S> treeVisualizer = new TreeVisualizer<S>(symbolTable, true);
        Map<Interval, Integer> terminals;
        TableVisualizer<S> tableVisualizer;
        int terminalsNum = -1;
        int depth = 0;
        int[] usedCells = new int[esa.getSequence().size()];
        while (terminalsNum != 0) {
            drawing = new TikzIntervalDrawing(buffer, String.format(caption, depth), String.format(label, depth), 0.65,
                    0.65, 26);
            terminals = treeVisualizer.visualizeTree(drawing, esa, depth);
            tableVisualizer = new TableVisualizer<S>(esa.getSequence(), symbolTable);
            tableVisualizer.visualizeChildTable(drawing, esa, terminals, usedCells, depth);
            drawing.flush();
            depth++;
            terminalsNum = terminals.size();
        }
    }
    
    public static void visualizeCorpus(Appendable buffer, String caption, String label, String srcFileName,
            String dstFileName, int windowSize) throws IOException {
        TikzVectorDrawing drawing = new TikzVectorDrawing(buffer, caption, label, 4, 0);
        drawing.setDocumentWidth(10);
        VectorVisualizer vectorVisualizer = new VectorVisualizer(srcFileName, dstFileName, windowSize);
        vectorVisualizer.visualizeCorpus(drawing);
        drawing.flush();
    }
    
    public static void visualizeDocuments(Appendable buffer, String caption, String label, String srcFileName,
            String dstFileName, int windowSize) throws IOException {
        TikzVectorDrawing drawing = new TikzVectorDrawing(buffer, caption, label, 4, 0.7);
        drawing.setDocumentWidth(8);
        VectorVisualizer vectorVisualizer = new VectorVisualizer(srcFileName, dstFileName, windowSize);
        vectorVisualizer.visualizeDocuments(drawing);
        drawing.flush();
    }
    
    public static void visualizeParallelCorpus(Appendable buffer, String caption, String label, String srcFileName,
            String dstFileName, int windowSize) throws IOException {
        TikzVectorDrawing drawing = new TikzVectorDrawing(buffer, caption, label, 4, 0);
        drawing.setDocumentWidth(4);
        VectorVisualizer vectorVisualizer = new VectorVisualizer(srcFileName, dstFileName, windowSize);
        vectorVisualizer.visualizeParallelCorpus(drawing);
        drawing.flush();
    }
    
    public static void visualizeParallelDocuments(Appendable buffer, String caption, String label, String srcFileName,
            String dstFileName, int windowSize) throws IOException {
        TikzVectorDrawing drawing = new TikzVectorDrawing(buffer, caption, label, 4, 1.2);
        drawing.setDocumentWidth(4);
        VectorVisualizer vectorVisualizer = new VectorVisualizer(srcFileName, dstFileName, windowSize);
        vectorVisualizer.visualizeParallelDocuments(drawing);
        drawing.flush();
    }
    
    public static void visualizeTermDocumentMatrix(Appendable buffer, String caption, String label, String srcFileName,
            String dstFileName, int windowSize) throws IOException {
        TikzVectorDrawing drawing = new TikzVectorDrawing(buffer, caption, label, 4, 0.4);
        VectorVisualizer vectorVisualizer = new VectorVisualizer(srcFileName, dstFileName, windowSize);
        vectorVisualizer.visualizeTermDocumentMatrix(drawing);
        drawing.flush();
    }
    
    public static void visualizeWordContextMatrix(Appendable buffer, String caption, String label, String srcFileName,
            String dstFileName, int windowSize) throws IOException {
        TikzVectorDrawing drawing = new TikzVectorDrawing(buffer, caption, label, 4, 0.4);
        VectorVisualizer vectorVisualizer = new VectorVisualizer(srcFileName, dstFileName, windowSize);
        vectorVisualizer.visualizeWordContextMatrix(drawing);
        drawing.flush();
    }

    public static void visualizeRepeats(Appendable buffer, String caption, String label, String srcFileName,
            String dstFileName, List<String> labels, int windowSize) throws IOException {
        TikzVectorDrawing drawing = new TikzVectorDrawing(buffer, caption, label, 4, 1.2);
        drawing.setDocumentWidth(4);
        VectorVisualizer vectorVisualizer = new VectorVisualizer(srcFileName, dstFileName, windowSize);
        vectorVisualizer.visualizeRepeats(drawing, labels);
        drawing.flush();
    }
    
    public static void visualizeVectors(Appendable buffer, String caption, String label, String srcFileName,
            String dstFileName, int windowSize) throws IOException {
        TikzVectorDrawing drawing = new TikzVectorDrawing(buffer, caption, label, 4, 1.2);
        drawing.setDocumentWidth(4);
        VectorVisualizer vectorVisualizer = new VectorVisualizer(srcFileName, dstFileName, windowSize);
        vectorVisualizer.visualizeVectors(drawing);
        drawing.flush();
    }
}
